package com.sunday.common.netty.e1_Hello_World;

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.util.Scanner;

/**
 * https://zhuanlan.zhihu.com/p/657817791
 * <p>
 * Discards any incoming data.
 * <p>
 * 核心组件
 * Netty的核心组件包括三类：
 * <p>
 * 引导类
 * 针对网络抽象的组件
 * 管理数据流及执行应用程序处理逻辑的组件
 * <p>
 * 2.1 引导类
 * Bootstrap: 用于客户端，连接到远程主机和端口，1个EventLoopGroup
 * ServerBootstrap: 用于服务器，绑定到一个本地端口，2个EventLoopGroup
 * <p>
 * 2.2 针对网络抽象的组件：
 * Channel: 对应Socket。基本的IO操作(bind(), connet(), read(), write()), 依赖于底层网络传输，大大降低了直接使用Socket的复杂性。Channel接口常用的实现类有：EmbeddedChannel, LocalServerChannel, NioDatagramChannel, NioSctpChannel, NioSocketChannel
 * EventLoop: 控制流、多线程处理、并发，用于处理连接的生命周期中所发生的事件。
 * ChannelFuture: 异步通知。Netty提供了ChannelFuture接口，通过addListener()方法注册ChannelFutureListener，以便在操作完成时得到通知。
 * <p>
 * 2.3 管理数据流及执行应用程序处理逻辑的组件
 * ChannelHandler: 处理入站和出站数据。ChannelInboundHandler处理入站操作(常见的实现类：SimpleChannelInboundHandler<T> ), ChannelOutboundHandler处理出站操作。程序员处理出入站数据的应用程序逻辑，都在ChannelHandler接口的实现里。
 * ChannelPipeline: 提供了 ChannelHandler 链的容器，并定义了用于在该链上传播人站和出站事件流的 API。当Channel 被创建时，它会被自动地分配到它专属的 ChannelPipeline。ChannelHandler 安装到 ChannelPipeline 中的过程如下所示：
 * 一个channelInitializer的实现被注册到了ServerBootstrap中
 * 当channelInitializer.initChannel()方法被调用时，ChannelInitializer将在ChannelPipeline 中安装一组自定义的 ChannelHandler;
 * ChannelInitializer 将它自己从 ChannelPipeline 中移除。
 */
@Slf4j
@AllArgsConstructor
public class DiscardClient {

    public static final String SERVER_HOST = "localhost";
    public static final int SERVER_PORT = 8811;

    public static void main(String[] args) throws Exception {

        /**
         * Bootstrap: 用于客户端，连接到远程主机和端口，1个EventLoopGroup
         */
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            Bootstrap bootstrap = new Bootstrap()
                    .group(workerGroup)
                    .channel(NioSocketChannel.class)
                    .option(ChannelOption.SO_KEEPALIVE, true)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new DiscardClientHandler());
                        }
                    });
            ChannelFuture channelFuture = bootstrap.connect(SERVER_HOST, SERVER_PORT).sync();
            log.info("Client started.");

            log.info("Input message: ");

            while (true) {
                Scanner reader = new Scanner(System.in);
                String input = reader.nextLine();
                if ("quit".equalsIgnoreCase(input)) {
                    break;
                }
                ByteBuf byteBuf = Unpooled.buffer().writeBytes(input.getBytes());
                channelFuture.channel().writeAndFlush(byteBuf);
            }
            channelFuture.channel().close();
            channelFuture.channel().closeFuture().sync();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            workerGroup.shutdownGracefully();
        }

    }
}